﻿using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Business.Solr {
    public class SolrClient {
        private const string COMMIT_COMMAND = "\"commit\": {}";
        private readonly static JsonSerializerSettings JsonSerializerSettings;
        private readonly string _UpdateUrl;
        private readonly string _SelectUrl;
        
        static SolrClient() {
            JsonSerializerSettings = InitJsonSerializerSettings();
        }

        private static JsonSerializerSettings InitJsonSerializerSettings() {
            var setting = new JsonSerializerSettings();
            setting.NullValueHandling = NullValueHandling.Ignore;

            var dateTimeConverter = new IsoDateTimeConverter();
            // 使用自定义日期格式，如果不使用的话，默认是ISO8601格式
            dateTimeConverter.DateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ";
            setting.Converters.Add(dateTimeConverter);

            return setting;
        }

        public SolrClient(string updateUrl, string selectUrl) {
            _UpdateUrl = updateUrl;
            _SelectUrl = selectUrl;
        }

        /// <summary>
        /// 根据查询参数构建访问solr的url
        /// </summary>
        /// <param name="query">查询参数</param>
        /// <returns>url</returns>
        private string BuildUrl(SolrQuery query) {
            var parameters = query.BuildUrlParameters();
            return new StringBuilder()
                .Append(_SelectUrl)
                .Append(_SelectUrl.Contains("?") ? "&" : "?")
            .Append(string.Join("&", parameters))
            .ToString();
        }

        public SolrResponse<T> Query<T>(SolrQuery query, int retryTimes = 0) {

            using (var client = new WebClientWithCompression()) {
                client.Encoding = Encoding.UTF8;

                var requestUrl = BuildUrl(query);

                string response;
                try {
                    response = client.DownloadString(requestUrl);
                }
                catch (WebException e) {
                    if (e.Response == null) {
                        // 数据链路级错误，进行重试
                        retryTimes++;
                        if (retryTimes > 5)
                            throw new ApplicationException(string.Format("无法连接到solr服务器：{0}", requestUrl), e);

                        var waitTime = retryTimes * 200;
                        //SearchLogger.LogSearchWarn(e, "无法连接到solr服务器：{0}，{1}ms后开始第{2}次重试", RequestUrl, waitTime, retryTimes);
                        Thread.Sleep(waitTime);
                        return Query<T>(query, retryTimes);
                    }
                    else {
                        // HTTP层错误，如solr返回非200HTTP响应，不需要重试，直接返回错误消息
                        using (var reader = new StreamReader(e.Response.GetResponseStream())) {
                            response = reader.ReadToEnd();
                        }
                    }
                }

                SolrResponse<T> solrResponse;
                try {
                    solrResponse = JsonConvert.DeserializeObject<SolrResponse<T>>(response);
                }
                catch (Exception e) {
                    throw new ApplicationException(string.Format("无法反序列化solr响应：{0}", response), e);
                }

                if (solrResponse.Header.Status != 0)
                    throw new ApplicationException(string.Format("solr返回错误响应：{0}", solrResponse.Error));

                return solrResponse;
            }
        }

        private string SerializeObject(object obj) {
            return JsonConvert.SerializeObject(obj, Formatting.None, JsonSerializerSettings);
        }

        private void ExecuteCommand(string command, int retryTimes = 0) {
            using (var client = new WebClientWithCompression()) {
                client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
                client.Encoding = Encoding.UTF8;

                try {
                    client.UploadString(_UpdateUrl, command);
                    // httpstatus = 200表示处理成功
                }
                catch (WebException e) {
                    if (e.Response == null) {
                        // 数据链路级错误，进行重试
                        retryTimes++;
                        if (retryTimes > 5)
                            throw new ApplicationException(string.Format("无法连接到solr服务器：{0}", _UpdateUrl), e);

                        var waitTime = retryTimes * 200;
                        //IndexingLogger.LogIndexingWarning(e, "无法连接到solr服务器：{0}，{1}ms后开始第{2}次重试", _SolrUpdateUrl, waitTime, retryTimes);
                        Thread.Sleep(waitTime);
                        ExecuteCommand(command, retryTimes);
                    }
                    else {
                        // HTTP层错误，如solr返回非200HTTP响应，不需要重试，直接返回错误消息
                        using (var reader = new StreamReader(e.Response.GetResponseStream())) {
                            var errorResponse = reader.ReadToEnd();
                            throw new ApplicationException(string.Format("solr返回错误响应：{0}", errorResponse));
                        }
                    }
                }
            }
        }

        public void DeleteByQuery(string query) {
            // 记录方法执行时间
            var sw = new Stopwatch();
            sw.Start();
            // 记录方法执行抛出的异常
            Exception exceptionThrown = null;

            try {
                if (string.IsNullOrWhiteSpace(query))
                    return;

                var command = new StringBuilder();
                command.Append("{\"delete\":")
                    .Append(SerializeObject(new { query }))
                    .Append(",")
                    .Append(COMMIT_COMMAND)
                    .Append("}");

                ExecuteCommand(command.ToString());
            }
            catch (Exception e) {
                exceptionThrown = e;
                throw e;
            }
            finally {
                // 记录建索引的日志
                //IndexingLogger.LogIndexing(exceptionThrown, sw.Elapsed);
            }
        }

        

        private void AppendAddCommand(StringBuilder command, object o) {
            command.Append("\"add\":{\"doc\":").Append(SerializeObject(o)).Append("},").ToString();
        }

        public void Save(object value) {
            // 记录方法执行时间
            var sw = new Stopwatch();
            sw.Start();

            // 记录方法执行抛出的异常
            Exception exceptionThrown = null;

            try {
                var command = new StringBuilder();
                command.Append("{");

                // add command
                if (value is IEnumerable)
                    foreach (var doc in (IEnumerable)value) {
                        AppendAddCommand(command, doc);
                    }
                else
                    AppendAddCommand(command, value);

                // commit command
                command.Append(COMMIT_COMMAND);

                command.Append("}");

                ExecuteCommand(command.ToString());
            }
            catch (Exception e) {
                exceptionThrown = e;
                throw e;
            }
            finally {
                // 记录建索引的日志
                //IndexingLogger.LogIndexing(exceptionThrown, sw.Elapsed);
            }
        }
    }
}
